package com.mdp.workflow.bus.candidate;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.google.common.annotations.VisibleForTesting;
import com.mdp.core.entity.Tips;
import com.mdp.core.err.BizException;
import com.mdp.workflow.comm.tools.FlowModelUtils;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.delegate.DelegateExecution;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * {@link TaskCandidateStrategy} 的调用者，用于调用对应的策略，实现任务的候选人的计算
 *
 *@author 唛盟开源9哥
 */
@Slf4j
public class TaskCandidateInvoker {

    private final Map<com.mdp.workflow.comm.dict.CandidateStrategy, TaskCandidateStrategy> strategyMap = new HashMap<>();


    public TaskCandidateInvoker(List<TaskCandidateStrategy> strategyList ) {
        strategyList.forEach(strategy -> {
            TaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy);
            Assert.isNull(oldStrategy, "策略({}) 重复", strategy.getStrategy().getDescription());
        });
    }

    /**
     * 校验流程模型的任务分配规则全部都配置了
     * 目的：如果有规则未配置，会导致流程任务找不到负责人，进而流程无法进行下去！
     *
     * @param bpmnBytes BPMN XML
     */
    public void validateBpmnConfig(byte[] bpmnBytes) {
        BpmnModel bpmnModel = FlowModelUtils.getBpmnModel(bpmnBytes);
        assert bpmnModel != null;
        List<UserTask> userTaskList = FlowModelUtils.getBpmnModelElements(bpmnModel, UserTask.class);
        // 遍历所有的 UserTask，校验审批人配置
        userTaskList.forEach(userTask -> {
            try {
                // 1. 非空校验
                String strategy = FlowModelUtils.parseCandidateStrategy(userTask);
                String param = FlowModelUtils.parseCandidateParam(userTask);
                if (strategy == null) {
                    //throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
                }
                TaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy);
                if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) {
                    //throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName());
                }
                // 2. 具体策略校验
                getCandidateStrategy(strategy).validateParam(param);
            }catch (BizException e){
                Tips tips=e.getTips();
                tips.setMsg(String.format("流程节点【%s】检验不通过，原因:",userTask.getName())+tips.getMsg());
                throw e;
            }
        });
    }

    /**
     * 计算任务的候选人
     *
     * @param execution 执行任务
     * @return 用户编号集合
     */
    public Set<String> calculateUsers(DelegateExecution execution) {
        String strategy = FlowModelUtils.parseCandidateStrategy(execution.getCurrentFlowElement());
        String param = FlowModelUtils.parseCandidateParam(execution.getCurrentFlowElement());
        // 1.1 计算任务的候选人
        Set<String> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
        // 1.2 移除被禁用的用户
        removeDisableUsers(userIds);

        // 2. 校验是否有候选人
        if (CollUtil.isEmpty(userIds)) {
            log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
                    execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param);
            //throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
        }
        return userIds;
    }

    /**
     * 计算任务的候选人
     *
     * @param execution 执行任务
     * @return 用户编号集合
     */
    public Set<String> calculateUsers(DelegateExecution execution,String strategy, String param) {
        // 1.1 计算任务的候选人
        Set<String> userIds = getCandidateStrategy(strategy).calculateUsers(execution, param);
        // 1.2 移除被禁用的用户
        removeDisableUsers(userIds);

        // 2. 校验是否有候选人
        if (CollUtil.isEmpty(userIds)) {
            log.error("[calculateUsers][流程任务({}/{}/{}) 任务规则({}/{}) 找不到候选人]", execution.getId(),
                    execution.getProcessDefinitionId(), execution.getCurrentActivityId(), strategy, param);
            //throw exception(TASK_CREATE_FAIL_NO_CANDIDATE_USER);
        }
        return userIds;
    }

    @VisibleForTesting
    public void removeDisableUsers(Set<String> assigneeUserIds) {
        if (CollUtil.isEmpty(assigneeUserIds)) {
            return;
        }
//        Map<Long, WfSysUser> userMap = adminUserApi.getUserMap(assigneeUserIds);
//        assigneeUserIds.removeIf(id -> {
//            WfSysUser user = userMap.get(id);
//            return user == null || !CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus());
//        });
    }

    private TaskCandidateStrategy getCandidateStrategy(String strategy) {
        com.mdp.workflow.comm.dict.CandidateStrategy strategyEnum = com.mdp.workflow.comm.dict.CandidateStrategy.of(strategy);
        Assert.notNull(strategyEnum, "策略({}) 不存在", strategy);
        TaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum);
        Assert.notNull(strategyObj, "策略({}) 不存在", strategy);
        return strategyObj;
    }

}